using System;
using System.IO;
using System.Xml.Serialization;
using System.Collections;
using System.Text.RegularExpressions;

namespace cuberga.TSP {
	[XmlRoot("cityList")]
    public class CityList {
        private static CityList _instance;
        public static CityList  Instance  {
            get {
                if (_instance == null) {
                    _instance = new CityList();
                }
                return _instance;
            }
        }

        protected CityList() {
        }

        private Hashtable _cities = new Hashtable(); 

        public void Clear() {
            _cities.Clear();
        }

        public int PointsCount {
            get {
                return _cities.Count;
            }
        }

        public void Add(int index, City newCity) {
            _cities[index] = newCity;
        }
        

        public City this[int index]{
            get {
                return (City)_cities[index];
            }
        }

        #region XML
        [XmlElement("city")]
        private City[] Items {
            get {
                City[] items = new City[ _cities.Count ];
                for(int i = 0; i < _cities.Count; i++) {
                    items[i] = (City)_cities[i];
                }
                return items;
            }
            set {
                _cities.Clear();
                for(int i = 0; i < value.Length; i++) {
                    _cities.Add(i, value[i]);
                }
            }
        }

        public void SaveToXml(string fileName) {
            XmlSerializer s = new XmlSerializer( typeof(City[]) );
            TextWriter w = new StreamWriter( fileName );
            s.Serialize( w, Items );
            w.Close();

        }

        public void LoadFromFile(string fileName) {
            TextReader textReader = new StreamReader(fileName);    

            if (fileName.EndsWith(".xml")) {
                XmlSerializer xmlSerializer = new XmlSerializer( typeof(City[]) );
                Items = (City[])xmlSerializer.Deserialize(textReader);                
            }
            else {
                _cities.Clear();

                Regex   regex = new Regex(@" +(\d+) +(\d+) +(\d+).*");
                string  fileLine;

                while ((fileLine = textReader.ReadLine()) != null) {                    
                    Match reMatch = regex.Match(fileLine);
                    if (reMatch.Success) {                        
                        int cityNumber = int.Parse(reMatch.Groups[1].Value); // CUST NO. in Solomon tests
                        int cityX = int.Parse(reMatch.Groups[2].Value);
                        int cityY = int.Parse(reMatch.Groups[3].Value); 
                        _cities.Add(cityNumber, new City(cityX, cityY));
                    }                        
                }
            }

            textReader.Close(); // redundant
        }
        #endregion

        public double Distance(int fromCity, int toCity) {
            int deltaX = (this[fromCity].X - this[toCity].X);
            int deltaY = (this[fromCity].Y - this[toCity].Y);
            return Math.Sqrt(Math.Pow(deltaX, 2.0) + Math.Pow(deltaY, 2.0));
        }

        public double GetLength(int[] route) {
            if (route.Length < 2)
                return 0;
            
            foreach(int city in route)
                if ((city < 0) || (city >= _cities.Count)) 
                    throw new IndexOutOfRangeException("incorrect point in route");

            double result = 0;
            for(int i = 0; i < route.Length - 1; i++) {
                result += this.Distance(route[i], route[i+1]);
            }
            return result;
        }
	}
}

